/**
 *
 */
package jp.seraph.jsmf.codec;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;

import jp.seraph.jsmf.motion.EditableMotion;
import jp.seraph.jsmf.motion.EditableMotionManager;
import jp.seraph.jsmf.motion.Motion;
import jp.seraph.jsmf.motion.MotionManager;

/**
 *
 */
public class FileMotionManager implements EditableMotionManager, MotionManager {
    public FileMotionManager(String aBasePath, MotionCodec aCodec) {
        this(new File(aBasePath), aCodec);
    }

    public FileMotionManager(File aBaseDirectory, MotionCodec aCodec) {
        if (aBaseDirectory.isFile())
            throw new IllegalArgumentException("ファイルを表すFileオブジェクトが渡されました。");

        mMotionMap = new HashMap<String, EditableMotion>();
        mCodec = aCodec;

        mMotionDirectory = aBaseDirectory;
        if (!mMotionDirectory.exists())
            mMotionDirectory.mkdirs();

        this.loadMotion();
    }

    private void loadMotion() {
        FilenameFilter tFilter = new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.endsWith(SUFFIX);
            }
        };
        for (File tFile : mMotionDirectory.listFiles(tFilter)) {
            if (tFile.isFile())
                loadMotion(tFile);
        }
    }

    private void loadMotion(File aFile) {
        Reader tReader = null;
        String tName = aFile.getName().replaceAll(SUFFIX + "$", "");
        try {
            tReader = new InputStreamReader(new FileInputStream(aFile));

            mMotionMap.put(tName, (EditableMotion) mCodec.decode(tReader));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (tReader != null)
                    tReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // private PolynomialAngleVelocityCalculator.FunctionEntry
    // decodeFunctionEntry(JSONObject aObject){
    // return new
    // PolynomialAngleVelocityCalculator.FunctionEntry(ArgumentKind.fromInt(aObject.getInt(ARGUMENT)),
    // decodeFunction(aObject.getJSONArray(TERMS)));
    // }

    private File mMotionDirectory;

    private Map<String, EditableMotion> mMotionMap;

    private MotionCodec mCodec;

    private static final String SUFFIX = ".motion";

    public void save() throws IOException {
        for (String tMotionname : mMotionMap.keySet()) {
            this.save(tMotionname);
        }
    }

    private void save(String aName) throws IOException {
        Motion tMotion = this.getMotionAsTerminalType(aName);
        File tSaveFile = new File(mMotionDirectory, aName + SUFFIX);

        if (tSaveFile.exists())
            tSaveFile.delete();
        tSaveFile.createNewFile();

        Writer tWriter = null;
        try {
            tWriter = new OutputStreamWriter(new FileOutputStream(tSaveFile));
            mCodec.encode(tMotion, tWriter);
        } finally {
            if (tWriter != null)
                tWriter.close();
        }
    }

    /**
     * TODO saveまで実際にファイルの削除を行わないようにすること
     * @see jp.seraph.jsmf.motion.MotionManager#removeMotion(java.lang.String)
     */
    public boolean removeMotion(String aMotionName) {
        if (!mMotionMap.containsKey(aMotionName))
            return false;

        mMotionMap.remove(aMotionName);

        FileUtils.deleteQuietly(new File(mMotionDirectory, aMotionName + SUFFIX));

        return true;
    }

    /**
     *
     * @see jp.seraph.jsmf.motion.EditableMotionManager#removeAll()
     */
    public void removeAll() {
        List<String> tNames = this.getMotionNames();
        for (String tName : tNames) {
            this.removeMotion(tName);
        }
    }

    /**
     *
     * @see jp.seraph.jsmf.motion.EditableMotionManager#getEditableMotion(java.lang.String)
     */
    public EditableMotion getEditableMotion(String aMotionName) {
        return this.getMotionAsTerminalType(aMotionName);
    }

    /**
     *
     * @see jp.seraph.jsmf.motion.EditableMotionManager#setMotion(java.lang.String, jp.seraph.jsmf.motion.EditableMotion)
     */
    public void setMotion(String aMotionName, EditableMotion aMotion) {
        mMotionMap.put(aMotionName, aMotion);
    }

    /**
     *
     * @see jp.seraph.jsmf.motion.MotionManager#getMotion(java.lang.String)
     */
    public Motion getMotion(String aMotionName) {
        return this.getMotionAsTerminalType(aMotionName);
    }

    private EditableMotion getMotionAsTerminalType(String aMotionName) {
        try {
            Motion tResult = mMotionMap.get(aMotionName);
            if (tResult != null) {
                return (EditableMotion) tResult.clone();
            } else {
                return null;
            }
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(e);
        }
    }

    public List<String> getMotionNames() {
        List<String> tResult = new ArrayList<String>();
        for (String tMotionName : mMotionMap.keySet()) {
            tResult.add(tMotionName);
        }

        return tResult;
    }

    /**
     *
     * @see jp.seraph.jsmf.motion.MotionManager#containsMotion(java.lang.String)
     */

    public boolean containsMotion(String aMotionName) {
        return mMotionMap.containsKey(aMotionName);
    }
}
